<?php

namespace App\Services\Draw\PoolGroupObject;

use App\Repositories\BlindBox\PoolPrizeRepository;
use App\Repositories\LogRepository;
use App\Services\Tools\RedisService;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Collection;
use App\Exceptions\BasicException;

/**
 * 扭蛋抽奖实体
 * @author wrh
 */
class PublicPoolGroup extends PoolGroup
{

    public function getNewPoolGroupOld()
    {

        //生成新的group填充礼物并且加锁
        $this->poolModel->one(['id' => $this->pool_id], true);
        $newPoolGroupList = $this->poolGroupModel->listAll(['pool_id' => $this->pool_id, 'status' => 1], ['id']);
        if ($newPoolGroupList->isEmpty()) {
            return null;
        }
        $randomPoolGroup     = $this->randomGroup($newPoolGroupList);
        $this->group_id = $randomPoolGroup->id;

        return $this;
    }

    public function getNewPoolGroup()
    {
        $pool = $this->poolModel->one(['id' => $this->pool_id], true);
        $newPoolGroupList = $this->poolGroupModel
            ->where(['pool_id' => $this->pool_id, 'status' => 1])
            ->select(['id', 'sort'])->orderBy('sort', 'asc')->get();
        if ($newPoolGroupList->isEmpty()) {
            return null;
        }
        $current_sort = $this->poolGroupModel->where('id', $pool->current_group_id)->value('sort');

        $first = $newPoolGroupList->first();
        if (empty($current_sort)) {
            $group_id = $first->id;
        } else {
            $current_check = $newPoolGroupList->first(function ($value, $key) use ($current_sort) {
                return $value->sort > $current_sort;
            });
            if (empty($current_check)) {
                $group_id = $first->id;
            } else {
                $group_id = $current_check->id;
            }
        }
        if (empty($group_id)) {
            return null;
        }
        $this->group_id = $group_id;
        $this->gameLockTime = 20;

        return $this;
    }


    /**
     * 大礼物阈值抽奖（不抽大奖）
     * @param $redisKey 查询的rediskey值
     * @param $drawCount 抽奖次数
     * @param $gift_template_ids  不抽取的礼物模板id
     * @return array  废弃
     */
    public function bigGiftDrawPrize($redisKey, $drawCount, $gift_template_ids)
    {
        $redis = $this->redisObject;
        $set_number = 1;#抽中数量
        $groupPrizes = $groupPrizesNot = [];
        $i = 0;
        $i_step = 199;
        $is_break = false;
        while (true) {
            $info = $redis->lpop($redisKey);
            if (empty($info)) {
                break;
            }
            $info_ary = explode('_', $info);
            if (in_array($info_ary[1], $gift_template_ids)) {
                $groupPrizesNot[] = $info;
                continue;
            } else {
                $groupPrizes[] = $info;
            }
            if ($set_number == $drawCount) {
                break;
            }
            $set_number ++;
        }
        //不出的礼物，重新塞进redis里面
        if (!empty($groupPrizesNot)) {
            $redis->lpush($redisKey, array_reverse($groupPrizesNot));
        }
        return $groupPrizes;
    }

    //修正判断
    public function grantCompensate($poolGroup, $user, $drawCount, $tag, $poolConfig)
    {
        $supInfo = $gift_info = $appoint_groupPrizes = $appoint_gift_info = [];
        $result_gift_num = $top_num = 0;//修正的礼物次数 有固定给的礼物，减去对应数量的抽奖次数

        $supInfo['appoint_gift_id'] = 0;
        //用户被禁用后 没有「修正/指定/保底」补偿
        if ($user->status == 1) {
            //单抽没有指定补偿
            if ($drawCount > 1) {
                //指定补偿判断  每次抽奖只补偿一次，优先指定 从奖池里面扣
                $appoint_gift_info = $this->addPoolRepo->appointCompensate($user->user_id);
                if ($appoint_gift_info) {
                    $appoint_result = $this->getAppointResult($appoint_gift_info, $poolGroup, $result_gift_num, $appoint_groupPrizes, $supInfo);
                    $appoint_gift_info = $appoint_result['appoint_gift_info'];
                    $appoint_gift_info['type'] = 1;
                    $result_gift_num = $appoint_result['result_gift_num'];
                    $appoint_groupPrizes = $appoint_result['appoint_groupPrizes'];
                    $supInfo = $appoint_result['supInfo'];
                }
            }

            $game_type = 2;
            //游戏判定为盈利的话 依旧走该逻辑
            $result = $this->addPoolRepo->grantCompensate($user->user_id, $game_type, $tag);
            if ($result['status']) {
                $check_record = [];
                //从补池金额里面扣 凭空生成一个礼物给用户
                if (empty($appoint_groupPrizes) && !empty($result['gift_info'])) {
                    $result_gift_num += 1;
                    $gift_info = $result['gift_info'];
                    $supInfo['account_record'] = [
                        'game_type' => $game_type,
                        'add_money' => 0 - $gift_info['price'],
                        'config_id' => $result['is_whole'] ? -2 :0,
                    ];
                    $check_record['gift_id'] = $gift_info['id'];
                    $gift_info['count'] = 1;#礼物数量，默认为1
                }
                #修正池次数
                if ($result['count'] > 0) {
                    $check_record['add_count'] = $result['count'];
                }
                if ($result['clear_money'] > 0) {
                    $check_record['clear_money'] = $result['clear_money'];
                }
                if (!empty($check_record)) {
                    $check_record = array_merge([
                        'user_id' => $user->user_id,
                        'check_id' => $result['check_id'] ?? 0,
                        'type' => $result['type'],
                        'is_repeat' => $result['is_repeat'] ?? 1,
                    ], $check_record);
                    //游戏总判定的补偿记录
                    if ($result['is_whole']) {
                        $check_record['check_repair_id'] = $result['check_repair_id'];
                        $check_record['set_type'] = $result['set_type'];
                        $supInfo['check_record_whole'] = $check_record;
                    } else {
                        $check_record['game_type'] = $game_type;
                        $supInfo['check_record'] = $check_record;
                    }
                }
            }
            // 保底机制判断  没有补偿的礼物，且为公共池 弃用
            if (false && empty($result_gift_num) && $tag == 'public') {
                $config_key = strtoupper($this->privateKey).'_GUARANTEED';
                $appoint_gift_info = $this->addPoolRepo->check_guaranteed($user, $config_key, $this->cachePrefix, $poolGroup);
                if ($appoint_gift_info) {
                    $appoint_result = $this->getAppointResult($appoint_gift_info, $poolGroup, $result_gift_num, $appoint_groupPrizes, $supInfo);
                    $appoint_gift_info = $appoint_result['appoint_gift_info'];
                    $appoint_gift_info['type'] = 2;
                    $result_gift_num = $appoint_result['result_gift_num'];
                    $appoint_groupPrizes = $appoint_result['appoint_groupPrizes'];
                    $supInfo = $appoint_result['supInfo'];
                }
            }
            //
            $whole_key = 'whole_game:draw_set_user';
            if ($this->redisObject->hexists($whole_key, $user->user_id)) {
                $redisKey = $this->cachePrefix . "prizeList_" . $this->tag;
                $no_gift_template_ids = [];
                $appoint_gt_id = $this->redisObject->hget($whole_key, $user->user_id);
                if ($this->tag != 'novice') {
                    $no_gift_template_ids = $this->addPoolRepo->checkBigGift($user->user_id);
                }
                $appoint_key = $poolGroup->group_id .'_'.$appoint_gt_id;
                if (!in_array($appoint_gt_id, $no_gift_template_ids) &&
                    $this->redisObject->set($this->cachePrefix . 'set_lock', 'lock', 'ex', 20, 'nx') &&
                    $this->redisObject->lrem($redisKey, -1, $appoint_key)) {
                    $drawCount -= 1;
                    $appoint_groupPrizes = [$appoint_key];
                    $appoint_gt_info = $this->giftTemplateModel->where('id', $appoint_gt_id)->with('gift')->first();
                    if (!empty($appoint_gt_info) && $appoint_gt_info->gift->price >= $poolConfig->top_gift_coin) {
                        $top_num = 1;
                    }
                    $this->redisObject->hdel($whole_key, $user->user_id);
                }
            }

            //是否可以强制触发碎片池 其他补偿未触发、且是公共池
            if (empty($result_gift_num) && $tag == 'public' && $this->forceTriggerFragment($user, $poolConfig)) {
                //触发成功
                $force_result = $this->execFragmentGroup($user, $poolGroup);
                $result_gift_num = $force_result['result_gift_num'];
                $appoint_groupPrizes = $force_result['appoint_groupPrizes'];
                if ($result_gift_num) {
                    $this->setFragmentForceTriggerInfo($user, $poolConfig);
                    $supInfo['is_force_trigger'] = 1;
                }
            }
        }

        #奖池可以抽取的数量
        $drawCount -= $result_gift_num;

        $supInfo['appoint_gift_info'] = $appoint_gift_info;//目前无效 后续没用到
        $supInfo['result_gift_num'] = $result_gift_num;
        $supInfo['gift_info'] = $gift_info;//「补偿」的礼物信息 凭空产生的

        if (!empty($gift_info) && $gift_info['price'] >= $poolConfig->top_gift_coin) {
            $top_num = 1;
        }
        return [
            'drawCount' => $drawCount,
            'supInfo' => $supInfo,
            'appoint_groupPrizes' => $appoint_groupPrizes,
            'top_num' => $top_num,
        ];
    }


    /**
     * 单抽判断  不出大于500000的礼物 「废弃」
     * @param $redisKey
     * @param array $not_draw_template_ids 不出的礼物
     * @return array
     */
    public function singleDrawPrize($redisKey, $not_draw_template_ids = [])
    {
        $redis = $this->redisObject;
        $compare_template_ids = $this->giftTemplateModel->whereHas('gift', function ($query) {
            $query->where('price', '>', 500000);
        })->pluck('id')->toArray();
        $gift_template_ids = array_unique(array_merge($compare_template_ids, $not_draw_template_ids));
        $groupPrizes = [];
        $gift_info = $redis->lrange($redisKey, 0, 300);
        foreach ($gift_info as $info) {
            $info_ary = explode('_', $info);
            if (in_array($info_ary[1], $gift_template_ids)) {
                continue;
            }
            $redis->lrem($redisKey, 1, $info);
            $groupPrizes[] = $info;
            break;
        }
        return $groupPrizes;
    }

    /**
     * @param $user
     * @param $redisKey
     * @param $drawCount
     * @param $poolConfig
     * @param int $top_num 顶级礼物初始数量
     * @return array
     */
    public function publicDrawPrize($user, $redisKey, $drawCount, $poolConfig, $top_num = 0)
    {
        //不出的礼物
        $not_draw_template_ids = [];
        if ($user->status == 0) {//用户被封禁
            $check_draw_coin = $poolConfig->big_gift_coin;
            /*$not_draw_template_ids = $this->giftTemplateModel->whereHas('gift', function ($query) use ($check_draw_coin) {
                $query->where('price', '>', $check_draw_coin);
            })->pluck('id')->toArray();*/
            $not_draw_template_ids = $this->getTemplateIds($check_draw_coin, 3);
        }
        #获取礼物阈值的包含的礼物
        $gift_template_ids = [];
        //新手池不设礼物阈值限制 2024.04.09
        if ($this->tag != 'novice') {
            $gift_template_ids = $this->addPoolRepo->checkBigGift($user->user_id);
            $not_draw_template_ids = array_unique(array_merge($not_draw_template_ids, $gift_template_ids));
        }
        //单抽判断  不出大于500000的礼物
        if ($drawCount == 1) {
            /*$compare_template_ids = $this->giftTemplateModel->whereHas('gift', function ($query) {
                $query->where('price', '>', 500000);
            })->pluck('id')->toArray();*/
            $compare_template_ids = $this->getTemplateIds(500000, 1);
            $not_draw_template_ids = array_unique(array_merge($compare_template_ids, $not_draw_template_ids));
        }
        $groupPrizes = $this->getDrawPrize($redisKey, $drawCount, $poolConfig->top_gift_coin, $not_draw_template_ids, $top_num);
            /*$groupPrizes = $this->redisObject->pipeline(function ($pipe) use ($drawCount) {
                for ($i = 0; $i < $drawCount; $i++) {
                    $pipe->lpop($this->cachePrefix . "prizeList_" . $this->tag);
                }
            });*/
        return $groupPrizes;
    }

    /**
     * @param $redisKey
     * @param $drawCount  抽奖次数
     * @param $top_gift_coin   顶级礼物 大于该价值的礼物 本次抽奖只能出一个
     * @param $not_draw_template_ids  不出的礼物
     * @param int $top_num  初始大礼物次数
     * @return array
     */
    public function getDrawPrize($redisKey, $drawCount, $top_gift_coin, $not_draw_template_ids, $top_num)
    {
        //该数组里面的礼物只能出一个
        $compare_template_ids = $this->getTemplateIds($top_gift_coin, 2);
        /*$compare_template_ids = $this->giftTemplateModel->whereHas('gift', function ($query) use ($top_gift_coin) {
            $query->where('price', '>', $top_gift_coin);
        })->pluck('id')->toArray();*/

        $redis = $this->redisObject;
        $set_number = 1;#抽中数量
        $groupPrizes  = $groupPrizesNot = [];
        $i = 0;
        $i_step = 199;
        $is_break = false;
//        $top_num = 0;#顶级礼物数量  一次抽奖只能出一个顶级礼物
        while (true) {
            $info = $redis->lpop($redisKey);
            if (empty($info)) {
                break;
            }
            $info_ary = explode('_', $info);
            if (in_array($info_ary[1], $not_draw_template_ids)) {
                $groupPrizesNot[] = $info;
                continue;
            }
            #记录顶级礼物数量
            if (in_array($info_ary[1], $compare_template_ids)) {
                if ($top_num == 0) {
                    $top_num++;
                } else {
                    $groupPrizesNot[] = $info;
                    continue;
                }
            }
            $groupPrizes[] = $info;
            if ($set_number == $drawCount) {
                break;
            }
            $set_number++;
        }
        //不出的礼物，重新塞进redis里面
        if (!empty($groupPrizesNot)) {
            $redis->lpush($redisKey, array_reverse($groupPrizesNot));
        }
        return $groupPrizes;

    }

    public function createNextRoundPrizesWeight($templatePrizes)
    {
        //随机礼物列表
        $sumCount  = $templatePrizes->sum('count');
        $prizeList = $templatePrizes->toArray();
        if ($sumCount < 1) {
            return false;
        }

        //根据剩余数量进行权重抽取
        $prizeId = 0;
        $groupId = 0;
        //权重总值
        $totalWeight = $sumCount;
        $randomId    = mt_rand(1, $totalWeight);
        $weight      = 0;
        $drawPrizes  = [];
        while ($sumCount > 0) {
            foreach ($prizeList as &$mPool) {
                if ($randomId > $weight && $randomId <= ($mPool['count'] + $weight)) {
                    $prizeId = $mPool['gift_template_id'];
                    $groupId = $mPool['group_id'];
                    $mPool['count']--;
                    $totalWeight--;
                    $weight = 0;
                    break;
                } else {
                    $weight += $mPool['count'];
                }
            }
            if ($totalWeight > 0) {
                $randomId = mt_rand(1, $totalWeight);
            }

            $drawPrizes[] = $groupId . "_" . $prizeId;
            $sumCount--;
        }

        //往redis list右边推送新的礼物列表
        $this->redisObject->rpush($this->cachePrefix . "prizeList_" . $this->tag, $drawPrizes);
        
        // 清空本轮礼物总数
//            $this->poolPrizeModel->updateData(['group_id' => $this->group_id], ['template_count' => 0]);
//            $this->poolPrizeModel->appendPrizes($this, $templatePrizes);

        //防止死锁添加悲观锁
        $this->poolPrizeModel->addLock(['group_id' => $this->group_id], ['template_count' => 0]);
        $this->poolPrizeModel->updateData(['group_id' => $this->group_id], ['template_count' => 0]);
        $this->poolPrizeModel->appendPrizes($this, $templatePrizes);

        return true;
    }


    private function getAppointResult($appoint_gift_info, $poolGroup, $result_gift_num, $appoint_groupPrizes, $supInfo)
    {
        $appoint_key = $poolGroup->group_id .'_'. $appoint_gift_info['gift_template_id'];
        if ($this->redisObject->lrem($this->cachePrefix . "prizeList_" . $this->tag, 1, $appoint_key)) {
            $result_gift_num += 1;
            $appoint_groupPrizes = [$appoint_key];
            $appoint_gift_info['count'] = 1;
            $supInfo['appoint_gift_id'] = $appoint_gift_info['appoint_gift_id'];
        } else {
            $appoint_gift_info = [];
        }
        return [
            'appoint_gift_info' => $appoint_gift_info,
            'result_gift_num' => $result_gift_num,
            'appoint_groupPrizes' => $appoint_groupPrizes,
            'supInfo' => $supInfo,
        ];
    }

    /**
     * @param $price 判定价格
     * @param int $type 1:新手池 2:顶级礼物一次只能出一个top_gift_coin 3:大礼物 用户被封禁不能出big_gift_coin
     * @return mixed
     */
    private function getTemplateIds($price, $type = 1)
    {
        switch ($type) {
            case 2:
                $redis_key = 'template:topGift';
                break;
            case 3:
                $redis_key = 'template:bigGift';
                break;
            default:
                $redis_key = 'template:single';
        }
        $redis_key = $this->cachePrefix.$redis_key;
        if ($this->redisObject->exists($redis_key)) {
            return json_decode($this->redisObject->get($redis_key));
        }

        $template_ids = $this->giftTemplateModel->whereHas('gift', function ($query) use ($price) {
            $query->where('price', '>', $price);
        })->pluck('id')->toArray();
        $this->redisObject->set($redis_key, json_encode($template_ids));
        return $template_ids;
    }

    public function drawPrizes(Collection $poolPrizeList, $poolConfig, $poolGroup, $user, $drawCount)
    {
        //判断是否需要修正
        $grant = $this->grantCompensate($poolGroup, $user, $drawCount, $this->tag, $poolConfig);
        $drawCount = $grant['drawCount'];
        $supInfo = $grant['supInfo'];
        $appoint_groupPrizes = $grant['appoint_groupPrizes'];
        $top_num = $grant['top_num'];
        $groupPrizes = [];
        $redisKey = $this->cachePrefix . "prizeList_" . $this->tag;

        #单抽没有补偿礼物  开始抽奖
        if ($drawCount > 0) {
            $groupPrizes = $this->publicDrawPrize($user, $redisKey, $drawCount, $poolConfig, $top_num);
        }

        $drawCount += count($appoint_groupPrizes);//加上指定补偿次数、补偿次数是从奖池出的
        $groupPrizes = array_merge($groupPrizes, $appoint_groupPrizes);

        $groupPrizes = array_filter($groupPrizes);
        $drawPrizes  = [];
        app('events')->listen('draw.redisRollback', function () use ($groupPrizes) {
            if (!empty($groupPrizes)) {
                $this->redisObject->lpush($this->cachePrefix . "prizeList_" . $this->tag, array_reverse($groupPrizes));
            }
        });

        if (count($groupPrizes) != $drawCount) {
            if (!empty($groupPrizes)) {
                $this->redisObject->lpush($this->cachePrefix . "prizeList_" . $this->tag, array_reverse($groupPrizes));
            }
            throw new BasicException(-1, '--服务繁忙,请稍后再试--');
        }
        foreach ($groupPrizes as $prize) {
            $prizeData = explode('_', $prize);
            if (!isset($drawPrizes[$prizeData[0]][$prizeData[1]])) {
                $drawPrizes[$prizeData[0]][$prizeData[1]] = 1;
            } else {
                $drawPrizes[$prizeData[0]][$prizeData[1]]++;
            }
        }
        $drawPrizes['supInfo'] = $supInfo;//游戏判定的数据

        return $drawPrizes;
    }

    protected function forceTriggerFragment($user, $poolConfig)
    {
        return false;
    }


    protected function getFragmentForceTriggerInfo($field = '')
    {
        return empty($field) ? '' : [];
    }

    protected function setFragmentForceTriggerInfo($user, $poolConfig)
    {
        return false;
    }

    protected function execFragmentGroup($user, $poolGroup)
    {
        return [];
    }
}
